﻿using AutoMapper;
using IOA.MES.DataAccess.Entity;
using IOA.MES.DataAccess.Enum;
using IOA.MES.DataAccess.ReportModel;
using IOA.MES.DataAccess.VModel;
using IOA.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Transactions;

namespace IOA.MES.Business
{
    /// <summary>
    /// 报表管理（错误代码：142001）
    /// </summary>
    public class ReportMgr
    {
        private MESContext mesDb = new MESContext("MES");

        /// <summary>
        /// 生产过站统计
        /// </summary>
        /// <param name="workshopCode">车间代码</param>
        /// <param name="startTime">起始时间</param>
        /// <param name="endTime">截止时间</param>
        /// <returns>过站统计数据</returns>
        public List<RViaSiteStatisticsItem> QueryViaSiteStatistics(string workshopCode, int lineId, string orderNo, string workOrderNo, DateTime startTime, DateTime endTime)
        {
            var report = new List<RViaSiteStatisticsItem>();
            var distributeCountQuery = mesDb.DistributeCodes.AsNoTracking().Where(_ => _.WorkshopCode == workshopCode && _.CreateTime >= startTime && _.CreateTime < endTime && _.Status != DistributeCodeStatus.Recycled && _.Valid);
            var componentQuery = mesDb.Components.AsNoTracking().Where(_ => _.WorkshopCode == workshopCode && _.Valid);
            var unPackQuery = mesDb.PackProducts.AsNoTracking().Where(_ => _.WorkshopCode == workshopCode && _.ModifyTime >= startTime && _.ModifyTime < endTime && _.Valid == false);
            if (lineId > 0)
            {
                distributeCountQuery = distributeCountQuery.Where(_ => _.LineId == lineId);
            }
            if (!string.IsNullOrWhiteSpace(workOrderNo))
            {
                distributeCountQuery = distributeCountQuery.Where(_ => _.WorkOrderNo == workOrderNo);
                componentQuery = componentQuery.Where(_ => _.WorkOrderNo == workOrderNo);
                unPackQuery = unPackQuery.Where(_ => _.WorkOrderNo == workOrderNo);
            }
            else if (!string.IsNullOrWhiteSpace(orderNo))
            {
                distributeCountQuery = distributeCountQuery.Where(_ => _.OrderNo == orderNo);
                componentQuery = componentQuery.Where(_ => _.OrderNo == orderNo);
                unPackQuery = unPackQuery.Where(_ => _.OrderNo == orderNo);
            }
            var distributeCount = distributeCountQuery.Select(_ => _.Code).Distinct().Count();
            report.Add(new RViaSiteStatisticsItem { SiteType = "分片", QLevel = "", Count = distributeCount });

            var components = componentQuery.Where(_ => _.DistributeTime <= endTime && _.LastSiteTime >= startTime).Select(_ => new RComponentTimeModel
            {
                BarCode = _.BarCode,
                QLevel = _.QLevel,
                DistributeTime = _.DistributeTime,
                WeldingLineId = _.WeldingLineId,
                WeldingTime = _.WeldingTime,
                LaminatedLineId = _.LaminatedLineId,
                LaminatedTime = _.LaminatedTime,
                EL1LineId = _.EL1LineId,
                EL1Time = _.EL1Time,
                TopLineId = _.TopLineId,
                TopTime = _.TopTime,
                NQCLineId = _.NQCLineId,
                NQCTime = _.NQCTime,
                FramingLineId = _.FramingLineId,
                FramingTime = _.FramingTime,
                CleanLineId = _.CleanLineId,
                CleanTime = _.CleanTime,
                IVTestLineId = _.IVTestLineId,
                IVTestTime = _.IVTestTime,
                InsulatedResistanceLineId = _.InsulatedResistanceLineId,
                InsulatedResistanceTime = _.InsulatedResistanceTime,
                EL2LineId = _.EL2LineId,
                EL2Time = _.EL2Time,
                NameplatePrintingLineId = _.NameplatePrintingLineId,
                NameplatePrintingTime = _.NameplatePrintingTime,
                FinalInspectionLineId = _.FinalInspectionLineId,
                FinalInspectionTime = _.FinalInspectionTime,
                PackLineId = _.PackLineId,
                PackTime = _.PackTime
            }).ToList();
            var siteTypes = new CommonMgr().QueryEnumTypes(typeof(SiteType)).Where(_ => _.Key != (int)SiteType.Sort).ToList();
            foreach (var site in siteTypes)
            {
                var item = new RViaSiteStatisticsItem
                {
                    SiteType = !Enum.IsDefined(typeof(SiteType), (SiteType)site.Key) ? string.Empty : EnumConvert.ToDesc((SiteType)site.Key),
                    QLevel = string.Empty,
                };
                var siteCount = 0;
                switch ((SiteType)site.Key)
                {
                    case SiteType.Welding:
                        siteCount = components.Count(_ => _.WeldingTime >= startTime && _.WeldingTime < endTime && (lineId <= 0 ? true : _.WeldingLineId == lineId));
                        break;
                    case SiteType.Laminated:
                        siteCount = components.Count(_ => _.LaminatedTime >= startTime && _.LaminatedTime < endTime && (lineId <= 0 ? true : _.LaminatedLineId == lineId));
                        break;
                    case SiteType.EL1:
                        siteCount = components.Count(_ => _.EL1Time >= startTime && _.EL1Time < endTime && (lineId <= 0 ? true : _.EL1LineId == lineId));
                        break;
                    case SiteType.Top:
                        siteCount = components.Count(_ => _.TopTime >= startTime && _.TopTime < endTime && (lineId <= 0 ? true : _.TopLineId == lineId));
                        break;
                    case SiteType.NQC:
                        siteCount = components.Count(_ => _.NQCTime >= startTime && _.NQCTime < endTime && (lineId <= 0 ? true : _.NQCLineId == lineId));
                        break;
                    case SiteType.Framing:
                        siteCount = components.Count(_ => _.FramingTime >= startTime && _.FramingTime < endTime && (lineId <= 0 ? true : _.FramingLineId == lineId));
                        break;
                    case SiteType.Clean:
                        siteCount = components.Count(_ => _.CleanTime >= startTime && _.CleanTime < endTime && (lineId <= 0 ? true : _.CleanLineId == lineId));
                        break;
                    case SiteType.IVTest:
                        siteCount = components.Count(_ => _.IVTestTime >= startTime && _.IVTestTime < endTime && (lineId <= 0 ? true : _.IVTestLineId == lineId));
                        break;
                    case SiteType.InsulatedResistance:
                        siteCount = components.Count(_ => _.InsulatedResistanceTime >= startTime && _.InsulatedResistanceTime < endTime && (lineId <= 0 ? true : _.InsulatedResistanceLineId == lineId));
                        break;
                    case SiteType.EL2:
                        siteCount = components.Count(_ => _.EL2Time >= startTime && _.EL2Time < endTime && (lineId <= 0 ? true : _.EL2LineId == lineId));
                        break;
                    case SiteType.NameplatePrinting:
                        siteCount = components.Count(_ => _.NameplatePrintingTime >= startTime && _.NameplatePrintingTime < endTime && (lineId <= 0 ? true : _.NameplatePrintingLineId == lineId));
                        break;
                    case SiteType.FinalInspection:
                        siteCount = components.Count(_ => _.FinalInspectionTime >= startTime && _.FinalInspectionTime < endTime && (lineId <= 0 ? true : _.FinalInspectionLineId == lineId));
                        break;
                    case SiteType.Packing:
                        siteCount = components.Count(_ => _.PackTime >= startTime && _.PackTime < endTime && (lineId <= 0 ? true : _.PackLineId == lineId));
                        break;
                }
                item.Count = siteCount;
                report.Add(item);
            }

            var packComponents = components.Where(_ => _.PackTime >= startTime && _.PackTime < endTime && (lineId <= 0 ? true : _.PackLineId == lineId))
                .GroupBy(_ => _.QLevel).Select(_ => new { QLevel = _.Key, Count = _.Count() }).OrderBy(_ => _.QLevel).ToList();
            report.AddRange(packComponents.Select(_ => new RViaSiteStatisticsItem
            {
                SiteType = "包装等级分布",
                QLevel = _.QLevel,
                Count = _.Count
            }));

            var unpacks = unPackQuery.ToList();
            report.Add(new RViaSiteStatisticsItem
            {
                SiteType = "解包总数",
                QLevel = "",
                Count = unpacks.Select(_ => _.BarCode).Distinct().Count()
            });
            report.Add(new RViaSiteStatisticsItem
            {
                SiteType = "解包非本班数",
                QLevel = "",
                Count = unpacks.Where(_ => _.CreateTime < startTime).Select(_ => _.BarCode).Distinct().Count()
            });
            return report;
        }

        /// <summary>
        /// 碎片率统计
        /// </summary>
        /// <param name="workshopCode">车间代码</param>
        /// <param name="startTime">起始时间</param>
        /// <param name="endTime">截止时间</param>
        /// <returns>碎片率数据</returns>
        public List<RCompenRate> QueryCompenRates(string workshopCode, string orderNo, string workOrderNo, DateTime startTime, DateTime endTime)
        {
            var report = new List<RCompenRate>();
            var compensQuery = mesDb.CompenCellRecords.AsNoTracking().Where(_ => _.WorkshopCode == workshopCode && _.StartTime >= startTime && _.EndTime <= endTime && _.Valid);
            var componentQuery = mesDb.Components.AsNoTracking().Where(_ => _.WorkshopCode == workshopCode && _.Valid);
            if (!string.IsNullOrWhiteSpace(workOrderNo))
            {
                componentQuery = componentQuery.Where(_ => _.WorkOrderNo == workOrderNo);
                compensQuery = compensQuery.Where(_ => _.WorkOrderNo == workOrderNo);
            }
            else if (!string.IsNullOrWhiteSpace(orderNo))
            {
                componentQuery = componentQuery.Where(_ => _.OrderNo == orderNo);
                compensQuery = compensQuery.Where(_ => _.OrderNo == orderNo);
            }
            var components = componentQuery.Where(_ => _.DistributeTime <= endTime && _.LastSiteTime >= startTime)
                .Select(_ => new { _.BarCode, _.WorkOrderNo, _.WeldingTime, _.LaminatedTime }).ToList();
            var workOrderNos = components.Select(_ => _.WorkOrderNo).Distinct().ToList();
            var workOrders = mesDb.WorkOrders.AsNoTracking().Where(_ => workOrderNos.Contains(_.WorkOrderNo) && _.Valid).ToList();
            var compens = compensQuery.ToList();

            var molecular1 = components.Where(_ => _.WeldingTime >= startTime && _.WeldingTime < endTime)
                .Sum(c => workOrders.FirstOrDefault(_ => StringUtils.Equals(_.WorkOrderNo, c.WorkOrderNo))?.CellQuantity ?? 0);
            var molecular2 = components.Where(_ => _.LaminatedTime >= startTime && _.LaminatedTime < endTime)
                .Sum(c => workOrders.FirstOrDefault(_ => StringUtils.Equals(_.WorkOrderNo, c.WorkOrderNo))?.CellQuantity ?? 0);

            var reasons = new CommonMgr().QueryEnumTypes(typeof(FragmentType));
            foreach (var item in reasons)
            {
                report.Add(new RCompenRate
                {
                    CompenReason = item.Value,
                    Molecular = compens.Where(_ => (int)_.CompenReason == item.Key).Sum(_ => _.CellCount),
                    Denominator1 = molecular1,
                    Denominator2 = molecular2
                });
            }
            report.Add(new RCompenRate
            {
                CompenReason = "总碎片",
                Molecular = compens.Sum(_ => _.CellCount),
                Denominator1 = molecular1,
                Denominator2 = molecular2
            });
            report.ForEach(_ =>
            {
                _.Rate1 = _.Denominator1 <= 0 ? 0 : Convert.ToDecimal((_.Molecular / (double)_.Denominator1 * 100).ToString("0.00"));
                _.Rate2 = _.Denominator2 <= 0 ? 0 : Convert.ToDecimal((_.Molecular / (double)_.Denominator2 * 100).ToString("0.00"));
            });

            return report;
        }

        /// <summary>
        /// 合格率统计
        /// </summary>
        /// <param name="workshopCode">车间代码</param>
        /// <param name="startTime">起始时间</param>
        /// <param name="endTime">截止时间</param>
        /// <returns>合格率数据</returns>
        public List<RPassRateItem> QueryPassRates(string workshopCode, int lineId, string orderNo, string workOrderNo, DateTime startTime, DateTime endTime)
        {
            var report = new List<RPassRateItem>();
            var componentQuery = mesDb.Components.AsNoTracking().Where(_ => _.WorkshopCode == workshopCode && _.Valid);
            var downgradeQuery = mesDb.Components.AsNoTracking().Where(_ => _.WorkshopCode == workshopCode && _.ScrapTime >= startTime && _.ScrapTime < endTime && _.Valid);
            if (!string.IsNullOrWhiteSpace(orderNo))
            {
                componentQuery = componentQuery.Where(_ => _.OrderNo == orderNo);
                downgradeQuery = downgradeQuery.Where(_ => _.OrderNo == orderNo);
            }
            if (!string.IsNullOrWhiteSpace(workOrderNo))
            {
                componentQuery = componentQuery.Where(_ => _.WorkOrderNo == workOrderNo);
                downgradeQuery = downgradeQuery.Where(_ => _.WorkOrderNo == workOrderNo);
            }
            if (lineId > 0)
            {
                componentQuery = componentQuery.Where(_ => _.LineId == lineId);
                downgradeQuery = downgradeQuery.Where(_ => _.LineId == lineId);
            }
            var components = componentQuery.Where(_ => _.DistributeTime <= endTime && _.LastSiteTime >= startTime).Select(_ => new
            {
                _.BarCode,
                _.LaminatedTime,
                _.EL1Time,
                _.NQCTime,
                _.EL2Time,
                _.FinalInspectionTime,
                _.EL1OncePass,
                _.NQCOncePass,
                _.EL2OncePass,
                _.FinalInspectionOncePass
            }).ToList();
            var downgrades = downgradeQuery.Select(_ => new { _.BarCode, _.QLevel, _.DowngradeCode }).ToList();

            var curEL1Components = components.Where(_ => _.EL1Time >= startTime && _.EL1Time < endTime).ToList();
            report.Add(new RPassRateItem
            {
                Category = "EL1一次合格率",
                Denominator = curEL1Components.Count,
                Molecular = curEL1Components.Count(_ => _.EL1OncePass == true)
            });
            var curNQCComponents = components.Where(_ => _.NQCTime >= startTime && _.NQCTime < endTime).ToList();
            report.Add(new RPassRateItem
            {
                Category = "层压出料一次合格率",
                Denominator = curNQCComponents.Count,
                Molecular = curNQCComponents.Count(_ => _.NQCOncePass == true)
            });
            var curEL2Components = components.Where(_ => _.EL2Time >= startTime && _.EL2Time < endTime).ToList();
            report.Add(new RPassRateItem
            {
                Category = "EL2一次合格率",
                Denominator = curEL2Components.Count,
                Molecular = curEL2Components.Count(_ => _.EL2OncePass == true)
            });
            var curFinalComponents = components.Where(_ => _.FinalInspectionTime >= startTime && _.FinalInspectionTime < endTime).ToList();
            report.Add(new RPassRateItem
            {
                Category = "终检一次合格率",
                Denominator = curFinalComponents.Count,
                Molecular = curFinalComponents.Count(_ => _.FinalInspectionOncePass == true)
            });

            var laminatedComponents = components.Where(_ => _.LaminatedTime >= startTime && _.LaminatedTime < endTime).ToList();
            var fpyDowngrades = downgrades.Where(_ => !StringUtils.Equals(_.QLevel, "Q1") && !StringUtils.Equals(_.QLevel, "BF")).ToList();
            var badReasons = mesDb.BadReasons.AsNoTracking().Where(_ => _.Valid).ToList();
            report.Add(new RPassRateItem
            {
                Category = "FPY",
                Denominator = laminatedComponents.Count,
                Molecular = fpyDowngrades.Select(_ => _.BarCode.ToUpper().Trim()).Distinct().Count()
            });
            report.Add(new RPassRateItem
            {
                Category = "FPY（扣除来料部分）",
                Denominator = laminatedComponents.Count,
                Molecular = fpyDowngrades.Where(_ => badReasons.FirstOrDefault(b => StringUtils.Equals(_.DowngradeCode, b.ReasonCode))?.IsMaterielBad != true).Select(_ => _.BarCode.ToUpper().Trim()).Distinct().Count()
            });
            report.Add(new RPassRateItem
            {
                Category = "来料不良",
                Denominator = laminatedComponents.Count,
                Molecular = fpyDowngrades.Where(_ => badReasons.FirstOrDefault(b => StringUtils.Equals(_.DowngradeCode, b.ReasonCode))?.IsMaterielBad == true).Select(_ => _.BarCode.ToUpper().Trim()).Distinct().Count()
            });
            report.Add(new RPassRateItem
            {
                Category = "成品返修率",
                Denominator = curEL1Components.Count,
                Molecular = 0
            });

            var bfDowngrades = downgrades.Where(_ => StringUtils.Equals(_.QLevel, "BF")).ToList();
            report.Add(new RPassRateItem
            {
                Category = "组件报废",
                Denominator = curFinalComponents.Count,
                Molecular = bfDowngrades.Select(_ => _.BarCode.ToUpper().Trim()).Distinct().Count()
            });
            report.ForEach(_ =>
            {
                _.Rate = _.Denominator == 0 ? 0 : Convert.ToDecimal((_.Molecular / (double)_.Denominator * 100).ToString("0.00"));
            });

            report.FirstOrDefault(_ => _.Category == "FPY").Rate = 100 - report.FirstOrDefault(_ => _.Category == "FPY").Rate;
            report.FirstOrDefault(_ => _.Category == "组件报废").Rate = 100 - report.FirstOrDefault(_ => _.Category == "组件报废").Rate;

            return report;
        }

        /// <summary>
        /// 查询IV分布统计
        /// </summary>
        /// <param name="workshopCode">车间代码</param>
        /// <param name="startTime">起始时间</param>
        /// <param name="endTime">截止时间</param>
        /// <returns>IV分布统计</returns>
        public List<RPassRateItem> QueryIvDistributes(string workshopCode, int lineId, string orderNo, string workOrderNo, DateTime startTime, DateTime endTime)
        {
            var report = new List<RPassRateItem>();
            var query = mesDb.Components.AsNoTracking().Where(_ => _.WorkshopCode == workshopCode && _.IVTestTime >= startTime && _.IVTestTime < endTime && _.Valid);
            if (lineId > 0)
            {
                query = query.Where(_ => _.IVTestLineId == lineId);
            }
            if (!string.IsNullOrWhiteSpace(orderNo))
            {
                query = query.Where(_ => _.OrderNo == orderNo);
            }
            if (!string.IsNullOrWhiteSpace(workOrderNo))
            {
                query = query.Where(_ => _.WorkOrderNo == workOrderNo);
            }
            var components = query.Select(_ => new { _.BarCode, _.IVPowerLevel }).ToList();

            report.AddRange(components.GroupBy(_ => _.IVPowerLevel).Select(_ => new RPassRateItem
            {
                Category = _.Key?.ToString("0"),
                Molecular = _.Select(iv => iv.BarCode).Distinct().Count()
            }));
            report.ForEach(_ =>
            {
                _.Denominator = report.Sum(r => r.Molecular);
                _.Rate = _.Denominator == 0 ? 0 : Convert.ToDecimal((_.Molecular / (double)_.Denominator * 100).ToString("0.00"));
            });

            return report;
        }

        /// <summary>
        /// 查询实时产能
        /// </summary>
        /// <param name="workshopCode">车间代码</param>
        /// <param name="startTime">起始时间</param>
        /// <param name="endTime">截止时间</param>
        /// <returns>站点产能列表</returns>
        public RRealTimeCapacity GetRealTimeCapacity(string workshopCode, DateTime startTime, DateTime endTime)
        {
            var lines = mesDb.AssemblyLines.AsNoTracking().Where(_ => _.WorkshopCode == workshopCode && _.Valid).ToList();
            var siteTypes = new CommonMgr().QueryEnumTypes(typeof(SiteType)).Where(_ => _.Key != (int)SiteType.Sort).ToList();
            var distributeCodes = mesDb.DistributeCodes.AsNoTracking().Where(_ => _.WorkshopCode == workshopCode && _.CreateTime >= startTime && _.CreateTime < endTime && _.Status != DistributeCodeStatus.Recycled && _.Valid).ToList();
            var result = new RRealTimeCapacity
            {
                Lines = lines.Select(_ => _.LineName).Distinct().ToList(),
                SiteTypes = siteTypes.Select(_ => _.Value).Distinct().ToList()
            };
            result.SiteTypes.Insert(0, "分片");
            foreach (var line in lines)
            {
                var pair = new KeyValuePair<string, List<int>>(line.LineName, new List<int>());
                pair.Value.Add(distributeCodes.Count(_ => _.LineId == line.PkId));
                result.LineCapacities.Add(pair);
            }
            var components = mesDb.Components.AsNoTracking().Where(_ => _.WorkshopCode == workshopCode && _.DistributeTime <= endTime && _.LastSiteTime >= startTime && _.Valid).Select(_ => new RComponentTimeModel
            {
                BarCode = _.BarCode,
                QLevel = _.QLevel,
                DistributeTime = _.DistributeTime,
                WeldingLineId = _.WeldingLineId,
                WeldingTime = _.WeldingTime,
                LaminatedLineId = _.LaminatedLineId,
                LaminatedTime = _.LaminatedTime,
                EL1LineId = _.EL1LineId,
                EL1Time = _.EL1Time,
                TopLineId = _.TopLineId,
                TopTime = _.TopTime,
                NQCLineId = _.NQCLineId,
                NQCTime = _.NQCTime,
                FramingLineId = _.FramingLineId,
                FramingTime = _.FramingTime,
                CleanLineId = _.CleanLineId,
                CleanTime = _.CleanTime,
                IVTestLineId = _.IVTestLineId,
                IVTestTime = _.IVTestTime,
                InsulatedResistanceLineId = _.InsulatedResistanceLineId,
                InsulatedResistanceTime = _.InsulatedResistanceTime,
                EL2LineId = _.EL2LineId,
                EL2Time = _.EL2Time,
                NameplatePrintingLineId = _.NameplatePrintingLineId,
                NameplatePrintingTime = _.NameplatePrintingTime,
                FinalInspectionLineId = _.FinalInspectionLineId,
                FinalInspectionTime = _.FinalInspectionTime,
                PackLineId = _.PackLineId,
                PackTime = _.PackTime
            }).ToList();
            foreach (var site in siteTypes)
            {
                var curComponents = new List<RComponentTimeModel>();
                switch ((SiteType)site.Key)
                {
                    case SiteType.Welding:
                        curComponents = components.Where(_ => _.WeldingTime >= startTime && _.WeldingTime < endTime).ToList();
                        break;
                    case SiteType.Laminated:
                        curComponents = components.Where(_ => _.LaminatedTime >= startTime && _.LaminatedTime < endTime).ToList();
                        break;
                    case SiteType.EL1:
                        curComponents = components.Where(_ => _.EL1Time >= startTime && _.EL1Time < endTime).ToList();
                        break;
                    case SiteType.Top:
                        curComponents = components.Where(_ => _.TopTime >= startTime && _.TopTime < endTime).ToList();
                        break;
                    case SiteType.NQC:
                        curComponents = components.Where(_ => _.NQCTime >= startTime && _.NQCTime < endTime).ToList();
                        break;
                    case SiteType.Framing:
                        curComponents = components.Where(_ => _.FramingTime >= startTime && _.FramingTime < endTime).ToList();
                        break;
                    case SiteType.Clean:
                        curComponents = components.Where(_ => _.CleanTime >= startTime && _.CleanTime < endTime).ToList();
                        break;
                    case SiteType.IVTest:
                        curComponents = components.Where(_ => _.IVTestTime >= startTime && _.IVTestTime < endTime).ToList();
                        break;
                    case SiteType.InsulatedResistance:
                        curComponents = components.Where(_ => _.InsulatedResistanceTime >= startTime && _.InsulatedResistanceTime < endTime).ToList();
                        break;
                    case SiteType.EL2:
                        curComponents = components.Where(_ => _.EL2Time >= startTime && _.EL2Time < endTime).ToList();
                        break;
                    case SiteType.NameplatePrinting:
                        curComponents = components.Where(_ => _.NameplatePrintingTime >= startTime && _.NameplatePrintingTime < endTime).ToList();
                        break;
                    case SiteType.FinalInspection:
                        curComponents = components.Where(_ => _.FinalInspectionTime >= startTime && _.FinalInspectionTime < endTime).ToList();
                        break;
                    case SiteType.Packing:
                        curComponents = components.Where(_ => _.PackTime >= startTime && _.PackTime < endTime).ToList();
                        break;
                }
                foreach (var line in lines)
                {
                    var count = 0;
                    switch ((SiteType)site.Key)
                    {
                        case SiteType.Welding:
                            count = curComponents.Count(c => c.WeldingLineId == line.PkId);
                            break;
                        case SiteType.Laminated:
                            count = curComponents.Count(c => c.LaminatedLineId == line.PkId);
                            break;
                        case SiteType.EL1:
                            count = curComponents.Count(c => c.EL1LineId == line.PkId);
                            break;
                        case SiteType.Top:
                            count = curComponents.Count(c => c.TopLineId == line.PkId);
                            break;
                        case SiteType.NQC:
                            count = curComponents.Count(c => c.NQCLineId == line.PkId);
                            break;
                        case SiteType.Framing:
                            count = curComponents.Count(c => c.FramingLineId == line.PkId);
                            break;
                        case SiteType.Clean:
                            count = curComponents.Count(c => c.CleanLineId == line.PkId);
                            break;
                        case SiteType.IVTest:
                            count = curComponents.Count(c => c.IVTestLineId == line.PkId);
                            break;
                        case SiteType.InsulatedResistance:
                            count = curComponents.Count(c => c.InsulatedResistanceLineId == line.PkId);
                            break;
                        case SiteType.EL2:
                            count = curComponents.Count(c => c.EL2LineId == line.PkId);
                            break;
                        case SiteType.NameplatePrinting:
                            count = curComponents.Count(c => c.NameplatePrintingLineId == line.PkId);
                            break;
                        case SiteType.FinalInspection:
                            count = curComponents.Count(c => c.FinalInspectionLineId == line.PkId);
                            break;
                        case SiteType.Packing:
                            count = curComponents.Count(c => c.PackLineId == line.PkId);
                            break;
                    }
                    result.LineCapacities.FirstOrDefault(_ => string.Equals(_.Key, line.LineName, StringComparison.CurrentCultureIgnoreCase)).Value.Add(count);
                }
            }
            return result;
        }

        /// <summary>
        /// 获取小时产能
        /// </summary>
        /// <param name="workshopCode">车间代码</param>
        /// <param name="siteType">站点类型</param>
        /// <param name="startTime">开始时间</param>
        /// <param name="endTime">截至时间</param>
        /// <returns>小时产能</returns>
        public RGetHourCapacity GetHourCapacity(string workshopCode, int siteType, DateTime startTime, DateTime endTime)
        {
            var lines = mesDb.AssemblyLines.AsNoTracking().Where(_ => _.WorkshopCode == workshopCode && _.Valid).ToList();
            var result = new RGetHourCapacity
            {
                Lines = lines.Select(_ => _.LineName).Distinct().ToList(),
                Hours = new List<string>(),
                LineCapacities = lines.Select(_ => new KeyValuePair<string, List<int>>(_.LineName, new List<int>())).ToList()
            };

            var distributeCodes = siteType > 0 ? new List<DistributeCode>()
                : mesDb.DistributeCodes.AsNoTracking().Where(_ => _.WorkshopCode == workshopCode && _.CreateTime >= startTime && _.CreateTime < endTime && _.Status != DistributeCodeStatus.Recycled && _.Valid).ToList();

            var components = new List<Component>();
            if (siteType > 0)
            {
                var query = mesDb.Components.AsNoTracking().Where(_ => _.WorkshopCode == workshopCode && _.Valid);
                switch ((SiteType)siteType)
                {
                    case SiteType.Welding:
                        query = query.Where(_ => _.WeldingTime >= startTime && _.WeldingTime < endTime);
                        break;
                    case SiteType.Laminated:
                        query = query.Where(_ => _.LaminatedTime >= startTime && _.LaminatedTime < endTime);
                        break;
                    case SiteType.EL1:
                        query = query.Where(_ => _.EL1Time >= startTime && _.EL1Time < endTime);
                        break;
                    case SiteType.Top:
                        query = query.Where(_ => _.TopTime >= startTime && _.TopTime < endTime);
                        break;
                    case SiteType.NQC:
                        query = query.Where(_ => _.NQCTime >= startTime && _.NQCTime < endTime);
                        break;
                    case SiteType.Framing:
                        query = query.Where(_ => _.FramingTime >= startTime && _.FramingTime < endTime);
                        break;
                    case SiteType.Clean:
                        query = query.Where(_ => _.CleanTime >= startTime && _.CleanTime < endTime);
                        break;
                    case SiteType.IVTest:
                        query = query.Where(_ => _.IVTestTime >= startTime && _.IVTestTime < endTime);
                        break;
                    case SiteType.InsulatedResistance:
                        query = query.Where(_ => _.InsulatedResistanceTime >= startTime && _.InsulatedResistanceTime < endTime);
                        break;
                    case SiteType.EL2:
                        query = query.Where(_ => _.EL2Time >= startTime && _.EL2Time < endTime);
                        break;
                    case SiteType.NameplatePrinting:
                        query = query.Where(_ => _.NameplatePrintingTime >= startTime && _.NameplatePrintingTime < endTime);
                        break;
                    case SiteType.FinalInspection:
                        query = query.Where(_ => _.FinalInspectionTime >= startTime && _.FinalInspectionTime < endTime);
                        break;
                    case SiteType.Packing:
                        query = query.Where(_ => _.PackTime >= startTime && _.PackTime < endTime);
                        break;
                }
                components = query.ToList();
            }

            for (DateTime time = DateTime.Parse(startTime.ToString("yyyy-MM-dd HH:00:00")); time < endTime; time = time.AddHours(1))
            {
                result.Hours.Add(time.ToString("HH:00"));
                if (siteType == 0)
                {
                    foreach (var line in lines)
                    {
                        result.LineCapacities.FirstOrDefault(_ => StringUtils.Equals(_.Key, line.LineName)).Value.Add(distributeCodes.Where(_ => _.LineId == line.PkId && _.CreateTime >= time && _.CreateTime < time.AddHours(1)).GroupBy(_ => _.Code).Count());
                    }
                }
                else
                {
                    foreach (var line in lines)
                    {
                        var count = 0;
                        switch ((SiteType)siteType)
                        {
                            case SiteType.Welding:
                                count = components.Count(_ => _.WeldingLineId == line.PkId && _.WeldingTime >= time && _.WeldingTime < time.AddHours(1));
                                break;
                            case SiteType.Laminated:
                                count = components.Count(_ => _.LaminatedLineId == line.PkId && _.LaminatedTime >= time && _.LaminatedTime < time.AddHours(1));
                                break;
                            case SiteType.EL1:
                                count = components.Count(_ => _.EL1LineId == line.PkId && _.EL1Time >= time && _.EL1Time < time.AddHours(1));
                                break;
                            case SiteType.Top:
                                count = components.Count(_ => _.TopLineId == line.PkId && _.TopTime >= time && _.TopTime < time.AddHours(1));
                                break;
                            case SiteType.NQC:
                                count = components.Count(_ => _.NQCLineId == line.PkId && _.NQCTime >= time && _.NQCTime < time.AddHours(1));
                                break;
                            case SiteType.Framing:
                                count = components.Count(_ => _.FramingLineId == line.PkId && _.FramingTime >= time && _.FramingTime < time.AddHours(1));
                                break;
                            case SiteType.Clean:
                                count = components.Count(_ => _.CleanLineId == line.PkId && _.CleanTime >= time && _.CleanTime < time.AddHours(1));
                                break;
                            case SiteType.IVTest:
                                count = components.Count(_ => _.IVTestLineId == line.PkId && _.IVTestTime >= time && _.IVTestTime < time.AddHours(1));
                                break;
                            case SiteType.InsulatedResistance:
                                count = components.Count(_ => _.InsulatedResistanceLineId == line.PkId && _.InsulatedResistanceTime >= time && _.InsulatedResistanceTime < time.AddHours(1));
                                break;
                            case SiteType.EL2:
                                count = components.Count(_ => _.EL2LineId == line.PkId && _.EL2Time >= time && _.EL2Time < time.AddHours(1));
                                break;
                            case SiteType.NameplatePrinting:
                                count = components.Count(_ => _.NameplatePrintingLineId == line.PkId && _.NameplatePrintingTime >= time && _.NameplatePrintingTime < time.AddHours(1));
                                break;
                            case SiteType.FinalInspection:
                                count = components.Count(_ => _.FinalInspectionLineId == line.PkId && _.FinalInspectionTime >= time && _.FinalInspectionTime < time.AddHours(1));
                                break;
                            case SiteType.Packing:
                                count = components.Count(_ => _.PackLineId == line.PkId && _.PackTime >= time && _.PackTime < time.AddHours(1));
                                break;
                        }
                        result.LineCapacities.FirstOrDefault(_ => StringUtils.Equals(_.Key, line.LineName)).Value.Add(count);
                    }
                }
            }
            return result;
        }

        /// <summary>
        /// 查询质量报表
        /// </summary>
        /// <param name="workshopCode">车间代码</param>
        /// <param name="startTime">起始时间</param>
        /// <param name="endTime">截止时间</param>
        /// <returns>质量报表</returns>
        public List<RQualityReportItem> QueryQualityReports(int page, int limit, out int total
            , string workshopCode, int lineId, string orderNo, string workOrderNo, string cabinetNo, string packNo, string barCode, DateTime? startTime, DateTime? endTime)
        {
            var report = new List<RQualityReportItem>();
            var query = mesDb.Components.AsNoTracking().Where(_ => _.WorkshopCode == workshopCode && _.FinalInspectionTime.HasValue && _.Valid);
            if (!string.IsNullOrWhiteSpace(barCode))
            {
                query = query.Where(_ => _.BarCode == barCode);
            }
            else if (!string.IsNullOrWhiteSpace(packNo))
            {
                query = query.Where(_ => _.PackNo == packNo);
            }
            else if (!string.IsNullOrWhiteSpace(cabinetNo))
            {
                query = query.Where(_ => _.CabinetNo == cabinetNo);
            }
            else if (!string.IsNullOrWhiteSpace(workOrderNo))
            {
                query = query.Where(_ => _.WorkOrderNo == workOrderNo);
            }
            else if (!string.IsNullOrWhiteSpace(orderNo))
            {
                query = query.Where(_ => _.OrderNo == orderNo);
            }
            else if (startTime.HasValue && endTime.HasValue)
            {
                query = query.Where(_ => _.FinalInspectionTime.Value >= startTime.Value && _.FinalInspectionTime.Value < endTime.Value);
            }
            if (lineId > 0)
            {
                query = query.Where(_ => _.FinalInspectionLineId == lineId);
            }
            total = query.Count();
            var components = limit <= 0 ? query.ToList() : query.OrderBy(_ => _.CabinetNo).Skip((page - 1) * limit).Take(limit).ToList();

            var workOrderNos = report.Select(_ => _.WorkOrderNo).Distinct().ToList();
            var workOrders = mesDb.WorkOrders.AsNoTracking().Where(_ => workOrderNos.Contains(_.WorkOrderNo) && _.Valid).ToList();

            report.AddRange(components.Select(_ => new RQualityReportItem
            {
                BarCode = _.BarCode,
                Time = _.FinalInspectionTime.Value,
                Efficiency = _.CellEfficiency,
                ConversionEfficiency = _.ConversionEfficiency,
                SinglePower = _.SinglePiecePower,
                CellColor = _.CellColor,
                WorkOrderNo = _.WorkOrderNo,
                OrderNo = _.OrderNo,
                Schema = _.NameplateModel,
                CurrentLevel = _.IVCurrentLevel,
                QLevel = _.QLevel,
                ComponentColor = _.ComponentColor,
                NameplateModel = _.NameplateModel,
                BackPlanSpecs = _.BackPlanSpecs,
                AluFrameSpecs = _.AluFrameSpecs,
                JunBoxSpecs = _.JunBoxSpecs,
                BatterySpecs = _.BatterySpecs,
                BatteryFactory = _.BatteryFactory,
                IVTime = _.IVTestTime,
                PackTime = _.PackTime,
                FullPackCount = 30,
                CabinetNo = _.CabinetNo,
                PackNo = _.PackNo,
                Isc = _.Isc ?? 0,
                Voc = _.Voc ?? 0,
                Vm = _.Vm ?? 0,
                Im = _.Im ?? 0,
                FF = _.FF ?? 0,
                Pmax = _.Pmax ?? 0,
                Temp = _.Temp ?? 0,
                Ress = _.Ress ?? 0,
                Ressh = _.Ressh ?? 0,
                ModEff = (_.ModEff ?? 0) * 100,
                RatedPower = _.IVPowerLevel ?? 0,
                LightIntensity = _.SunRef ?? 0,
                SeriesResistance = _.Ress ?? 0,
                ParallelResistance = _.Ressh ?? 0,
                WeldingMachine = _.WeldingMachine,
                TheoreticalPower = _.TheoreticalPower,
                CellCompany = _.BatteryFactory,
                Duty = _.FinalInspectionDuty,
                Operator = _.FinalInspectionUser,
                CellModel = workOrders.LastOrDefault(r => string.Equals(_.WorkOrderNo, r.WorkOrderNo, StringComparison.CurrentCultureIgnoreCase))?.CellModel ?? string.Empty,
                CellLevel = workOrders.LastOrDefault(r => string.Equals(_.WorkOrderNo, r.WorkOrderNo, StringComparison.CurrentCultureIgnoreCase))?.CellLevel ?? string.Empty,
                CellPiece = workOrders.LastOrDefault(r => string.Equals(_.WorkOrderNo, r.WorkOrderNo, StringComparison.CurrentCultureIgnoreCase))?.CellQuantity ?? 0,
                BatteryBatchNo = _.BatteryBatchNo
            }));

            return report;
        }

        /// <summary>
        /// 查询漏扫情况
        /// </summary>
        /// <param name="workshopCode">车间代码</param>
        /// <param name="startTime">开始时间</param>
        /// <param name="endTime">截止时间</param>
        /// <returns>报表</returns>
        public List<KeyValuePair<string, int>> QueryMissingViaSiteCounts(string workshopCode, DateTime startTime, DateTime endTime)
        {
            var comonents = mesDb.Components.AsNoTracking().Where(_ => _.WorkshopCode == workshopCode && _.DistributeTime >= startTime && _.DistributeTime < endTime && _.Valid)
                .Select(_ => new
                {
                    _.BarCode,
                    _.WeldingTime,
                    _.LaminatedTime,
                    _.EL1Time,
                    _.TopTime,
                    _.NQCTime,
                    _.FramingTime,
                    _.CleanTime,
                    _.IVTestTime,
                    _.InsulatedResistanceTime,
                    _.EL2Time,
                    _.NameplatePrintingTime,
                    _.FinalInspectionTime,
                    _.PackTime
                }).ToList();
            var sites = new CommonMgr().QueryEnumTypes(typeof(SiteType)).Where(_ => _.Key != (int)SiteType.Sort).ToList();
            var report = new List<KeyValuePair<string, int>>();
            foreach (var site in sites)
            {
                var count = 0;
                switch ((SiteType)site.Key)
                {
                    case SiteType.Welding:
                        count = comonents.Count(_ => !_.WeldingTime.HasValue);
                        break;
                    case SiteType.Laminated:
                        count = comonents.Count(_ => !_.LaminatedTime.HasValue);
                        break;
                    case SiteType.EL1:
                        count = comonents.Count(_ => !_.EL1Time.HasValue);
                        break;
                    case SiteType.Top:
                        count = comonents.Count(_ => !_.TopTime.HasValue);
                        break;
                    case SiteType.NQC:
                        count = comonents.Count(_ => !_.NQCTime.HasValue);
                        break;
                    case SiteType.Framing:
                        count = comonents.Count(_ => !_.FramingTime.HasValue);
                        break;
                    case SiteType.Clean:
                        count = comonents.Count(_ => !_.CleanTime.HasValue);
                        break;
                    case SiteType.IVTest:
                        count = comonents.Count(_ => !_.IVTestTime.HasValue);
                        break;
                    case SiteType.InsulatedResistance:
                        count = comonents.Count(_ => !_.InsulatedResistanceTime.HasValue);
                        break;
                    case SiteType.EL2:
                        count = comonents.Count(_ => !_.EL2Time.HasValue);
                        break;
                    case SiteType.NameplatePrinting:
                        count = comonents.Count(_ => !_.NameplatePrintingTime.HasValue);
                        break;
                    case SiteType.FinalInspection:
                        count = comonents.Count(_ => !_.FinalInspectionTime.HasValue);
                        break;
                    case SiteType.Packing:
                        count = comonents.Count(_ => !_.PackTime.HasValue);
                        break;
                }
                report.Add(new KeyValuePair<string, int>(site.Value, count));
            }
            return report;
        }

        /// <summary>
        /// 查询漏扫情况
        /// </summary>
        /// <param name="workshopCode">车间代码</param>
        /// <param name="startTime">开始时间</param>
        /// <param name="endTime">截止时间</param>
        /// <returns>报表</returns>
        public List<KeyValuePair<string, List<string>>> QueryMissingViaSiteComponents(string workshopCode, DateTime startTime, DateTime endTime)
        {
            var comonents = mesDb.Components.AsNoTracking().Where(_ => _.WorkshopCode == workshopCode && _.DistributeTime >= startTime && _.DistributeTime < endTime && _.Valid)
                            .Select(_ => new
                            {
                                _.BarCode,
                                _.WeldingTime,
                                _.LaminatedTime,
                                _.EL1Time,
                                _.TopTime,
                                _.NQCTime,
                                _.FramingTime,
                                _.CleanTime,
                                _.IVTestTime,
                                _.InsulatedResistanceTime,
                                _.EL2Time,
                                _.NameplatePrintingTime,
                                _.FinalInspectionTime,
                                _.PackTime
                            }).ToList();

            var sites = new CommonMgr().QueryEnumTypes(typeof(SiteType)).Where(_ => _.Key != (int)SiteType.Sort).ToList();
            var report = new List<KeyValuePair<string, List<string>>>();
            foreach (var site in sites)
            {
                var missingCodes = new List<string>();
                switch ((SiteType)site.Key)
                {
                    case SiteType.Welding:
                        missingCodes.AddRange(comonents.Where(_ => !_.WeldingTime.HasValue).Select(_ => _.BarCode));
                        break;
                    case SiteType.Laminated:
                        missingCodes.AddRange(comonents.Where(_ => !_.LaminatedTime.HasValue).Select(_ => _.BarCode));
                        break;
                    case SiteType.EL1:
                        missingCodes.AddRange(comonents.Where(_ => !_.EL1Time.HasValue).Select(_ => _.BarCode));
                        break;
                    case SiteType.Top:
                        missingCodes.AddRange(comonents.Where(_ => !_.TopTime.HasValue).Select(_ => _.BarCode));
                        break;
                    case SiteType.NQC:
                        missingCodes.AddRange(comonents.Where(_ => !_.NQCTime.HasValue).Select(_ => _.BarCode));
                        break;
                    case SiteType.Framing:
                        missingCodes.AddRange(comonents.Where(_ => !_.FramingTime.HasValue).Select(_ => _.BarCode));
                        break;
                    case SiteType.Clean:
                        missingCodes.AddRange(comonents.Where(_ => !_.CleanTime.HasValue).Select(_ => _.BarCode));
                        break;
                    case SiteType.IVTest:
                        missingCodes.AddRange(comonents.Where(_ => !_.IVTestTime.HasValue).Select(_ => _.BarCode));
                        break;
                    case SiteType.InsulatedResistance:
                        missingCodes.AddRange(comonents.Where(_ => !_.InsulatedResistanceTime.HasValue).Select(_ => _.BarCode));
                        break;
                    case SiteType.EL2:
                        missingCodes.AddRange(comonents.Where(_ => !_.EL2Time.HasValue).Select(_ => _.BarCode));
                        break;
                    case SiteType.NameplatePrinting:
                        missingCodes.AddRange(comonents.Where(_ => !_.NameplatePrintingTime.HasValue).Select(_ => _.BarCode));
                        break;
                    case SiteType.FinalInspection:
                        missingCodes.AddRange(comonents.Where(_ => !_.FinalInspectionTime.HasValue).Select(_ => _.BarCode));
                        break;
                    case SiteType.Packing:
                        missingCodes.AddRange(comonents.Where(_ => !_.PackTime.HasValue).Select(_ => _.BarCode));
                        break;
                }
                report.Add(new KeyValuePair<string, List<string>>(site.Value, missingCodes));
            }
            return report;
        }

        /// <summary>
        /// 查询统计报表
        /// </summary>
        /// <param name="workshopCode">车间代码</param>
        /// <param name="orderNo">订单号</param>
        /// <param name="workOrderNo">工单号</param>
        /// <param name="needLevelPowers">等级功率分布</param>
        /// <param name="startTime">开始时间</param>
        /// <param name="endTime">截止时间</param>
        /// <returns>报表字典</returns>
        public Tuple<List<string>, List<Dictionary<string, object>>> QueryStatistics(string workshopCode, string orderNo, string workOrderNo, bool needLevelPowers, DateTime? startTime, DateTime? endTime)
        {
            if (string.IsNullOrWhiteSpace(workshopCode) || string.IsNullOrWhiteSpace(orderNo) && string.IsNullOrWhiteSpace(workOrderNo) && !startTime.HasValue && !endTime.HasValue)
            {
                return new Tuple<List<string>, List<Dictionary<string, object>>>(new List<string>(), new List<Dictionary<string, object>>());
            }

            var headers = new List<string>();
            var result = new List<Dictionary<string, object>>();

            var recordQuery = mesDb.ProductViaSiteRecords.AsNoTracking().Where(_ => _.WorkshopCode == workshopCode && _.Valid);
            var componentQuery = mesDb.Components.AsNoTracking().Where(_ => _.WorkshopCode == workshopCode && _.Valid);
            if (!string.IsNullOrWhiteSpace(workOrderNo))
            {
                recordQuery = recordQuery.Where(_ => _.WorkOrderNo == workOrderNo);
                componentQuery = componentQuery.Where(_ => _.WorkOrderNo == workOrderNo);
            }
            if (!string.IsNullOrWhiteSpace(orderNo))
            {
                recordQuery = recordQuery.Where(_ => _.OrderNo == orderNo);
                componentQuery = componentQuery.Where(_ => _.OrderNo == orderNo);
            }
            var hasTimeRange = startTime.HasValue && endTime.HasValue;

            var components = (!hasTimeRange ? componentQuery
                : componentQuery.Where(_ => _.DistributeTime <= endTime && _.LastSiteTime >= startTime)).Select(_ => new RComponentTimeModel
                {
                    BarCode = _.BarCode,
                    OrderNo = _.OrderNo,
                    WorkOrderNo = _.WorkOrderNo,
                    IVPowerLevel = _.IVPowerLevel,
                    QLevel = _.QLevel,
                    DistributeTime = _.DistributeTime,
                    WeldingLineId = _.WeldingLineId,
                    WeldingTime = _.WeldingTime,
                    LaminatedLineId = _.LaminatedLineId,
                    LaminatedTime = _.LaminatedTime,
                    EL1LineId = _.EL1LineId,
                    EL1Time = _.EL1Time,
                    TopLineId = _.TopLineId,
                    TopTime = _.TopTime,
                    NQCLineId = _.NQCLineId,
                    NQCTime = _.NQCTime,
                    FramingLineId = _.FramingLineId,
                    FramingTime = _.FramingTime,
                    CleanLineId = _.CleanLineId,
                    CleanTime = _.CleanTime,
                    IVTestLineId = _.IVTestLineId,
                    IVTestTime = _.IVTestTime,
                    InsulatedResistanceLineId = _.InsulatedResistanceLineId,
                    InsulatedResistanceTime = _.InsulatedResistanceTime,
                    EL2LineId = _.EL2LineId,
                    EL2Time = _.EL2Time,
                    NameplatePrintingLineId = _.NameplatePrintingLineId,
                    NameplatePrintingTime = _.NameplatePrintingTime,
                    FinalInspectionLineId = _.FinalInspectionLineId,
                    FinalInspectionTime = _.FinalInspectionTime,
                    PackLineId = _.PackLineId,
                    PackTime = _.PackTime,
                    ScrapTime = _.ScrapTime
                }).ToList();
            var siteTypes = new CommonMgr().QueryEnumTypes(typeof(SiteType)).Where(_ => _.Key != (int)SiteType.Sort).ToList();

            var orderNos = !hasTimeRange ? components.Select(_ => _.OrderNo).Distinct().ToList()
                : recordQuery.Where(_ => _.CreateTime >= startTime.Value && _.CreateTime < endTime.Value).Select(_ => _.OrderNo).Distinct().ToList();
            var workOrderNos = !hasTimeRange ? components.Select(_ => _.WorkOrderNo).Distinct().ToList()
                : recordQuery.Where(_ => _.CreateTime >= startTime.Value && _.CreateTime < endTime.Value).Select(_ => _.WorkOrderNo).Distinct().ToList();
            var workOrders = mesDb.WorkOrders.AsNoTracking().Where(_ => workOrderNos.Contains(_.WorkOrderNo) && _.Valid).ToList();
            var powerLevels = components.Where(_ => !hasTimeRange ? true : (_.IVTestTime >= startTime.Value && _.IVTestTime < endTime.Value))
                .Select(_ => _.IVPowerLevel).Distinct().ToList();
            var qLevels = components.Where(_ => !hasTimeRange ? true : (_.FinalInspectionTime >= startTime.Value && _.FinalInspectionTime < endTime.Value))
                .Select(_ => _.QLevel).Distinct().Where(_ => !string.Equals(_, "BF", StringComparison.CurrentCultureIgnoreCase)).ToList();
            qLevels.Add("BF");

            headers.AddRange(new List<string> { "订单号", "工单号", "计划产量", "电池片数", "分片" });
            headers.AddRange(siteTypes.Select(_ => _.Value));
            headers.AddRange(new List<string> { "封损", "未包装" });
            headers.AddRange(powerLevels.Select(_ => _.HasValue ? _.ToString() : "无功率"));
            headers.AddRange(qLevels.Select(_ => string.IsNullOrWhiteSpace(_) ? "无等级" : (string.Equals(_, "BF", StringComparison.CurrentCultureIgnoreCase) ? "报废" : _)));

            foreach (var curOrderNo in orderNos)
            {
                var curWorkOrders = workOrders.Where(_ => string.Equals(_.OrderNo, curOrderNo, StringComparison.CurrentCultureIgnoreCase)).ToList();
                var dict = new Dictionary<string, object>();
                dict.Add("订单号", curOrderNo);
                dict.Add("工单号", string.Join(",", curWorkOrders.Select(_ => _.WorkOrderNo)));
                dict.Add("计划产量", curWorkOrders.Sum(_ => _.Count));
                dict.Add("电池片数", curWorkOrders.Sum(_ => _.Count * _.CellQuantity));
                dict.Add("分片", components.Count(_ => string.Equals(_.OrderNo, curOrderNo, StringComparison.CurrentCultureIgnoreCase)
                    && (!hasTimeRange ? _.DistributeTime.HasValue : (_.DistributeTime >= startTime.Value && _.DistributeTime < endTime.Value))));

                var ivComponents = new List<RComponentTimeModel>();
                var finalComponents = new List<RComponentTimeModel>();
                foreach (var siteType in siteTypes)
                {
                    switch ((SiteType)siteType.Key)
                    {
                        case SiteType.Welding:
                            dict.Add(siteType.Value, components.Count(_ => string.Equals(_.OrderNo, curOrderNo, StringComparison.CurrentCultureIgnoreCase)
                                && (!hasTimeRange ? _.WeldingTime.HasValue : (_.WeldingTime >= startTime.Value && _.WeldingTime < endTime.Value))));
                            break;
                        case SiteType.Laminated:
                            dict.Add(siteType.Value, components.Count(_ => string.Equals(_.OrderNo, curOrderNo, StringComparison.CurrentCultureIgnoreCase)
                                && (!hasTimeRange ? _.LaminatedTime.HasValue : (_.LaminatedTime >= startTime.Value && _.LaminatedTime < endTime.Value))));
                            break;
                        case SiteType.EL1:
                            dict.Add(siteType.Value, components.Count(_ => string.Equals(_.OrderNo, curOrderNo, StringComparison.CurrentCultureIgnoreCase)
                                && (!hasTimeRange ? _.EL1Time.HasValue : (_.EL1Time >= startTime.Value && _.EL1Time < endTime.Value))));
                            break;
                        case SiteType.Top:
                            dict.Add(siteType.Value, components.Count(_ => string.Equals(_.OrderNo, curOrderNo, StringComparison.CurrentCultureIgnoreCase)
                                 && (!hasTimeRange ? _.TopTime.HasValue : (_.TopTime >= startTime.Value && _.TopTime < endTime.Value))));
                            break;
                        case SiteType.NQC:
                            dict.Add(siteType.Value, components.Count(_ => string.Equals(_.OrderNo, curOrderNo, StringComparison.CurrentCultureIgnoreCase)
                                 && (!hasTimeRange ? _.NQCTime.HasValue : (_.NQCTime >= startTime.Value && _.NQCTime < endTime.Value))));
                            break;
                        case SiteType.Framing:
                            dict.Add(siteType.Value, components.Count(_ => string.Equals(_.OrderNo, curOrderNo, StringComparison.CurrentCultureIgnoreCase)
                                && (!hasTimeRange ? _.FramingTime.HasValue : (_.FramingTime >= startTime.Value && _.FramingTime < endTime.Value))));
                            break;
                        case SiteType.Clean:
                            dict.Add(siteType.Value, components.Count(_ => string.Equals(_.OrderNo, curOrderNo, StringComparison.CurrentCultureIgnoreCase)
                                && (!hasTimeRange ? _.CleanTime.HasValue : (_.CleanTime >= startTime.Value && _.CleanTime < endTime.Value))));
                            break;
                        case SiteType.IVTest:
                            ivComponents = components.Where(_ => string.Equals(_.OrderNo, curOrderNo, StringComparison.CurrentCultureIgnoreCase)
                                && (!hasTimeRange ? _.IVTestTime.HasValue : (_.IVTestTime >= startTime.Value && _.IVTestTime < endTime.Value))).ToList();
                            dict.Add(siteType.Value, ivComponents.Count);
                            break;
                        case SiteType.InsulatedResistance:
                            dict.Add(siteType.Value, components.Count(_ => string.Equals(_.OrderNo, curOrderNo, StringComparison.CurrentCultureIgnoreCase)
                                 && (!hasTimeRange ? _.InsulatedResistanceTime.HasValue : (_.InsulatedResistanceTime >= startTime.Value && _.InsulatedResistanceTime < endTime.Value))));
                            break;
                        case SiteType.EL2:
                            dict.Add(siteType.Value, components.Count(_ => string.Equals(_.OrderNo, curOrderNo, StringComparison.CurrentCultureIgnoreCase)
                                && (!hasTimeRange ? _.EL2Time.HasValue : (_.EL2Time >= startTime.Value && _.EL2Time < endTime.Value))));
                            break;
                        case SiteType.NameplatePrinting:
                            dict.Add(siteType.Value, components.Count(_ => string.Equals(_.OrderNo, curOrderNo, StringComparison.CurrentCultureIgnoreCase)
                                && (!hasTimeRange ? _.NameplatePrintingTime.HasValue : (_.NameplatePrintingTime >= startTime.Value && _.NameplatePrintingTime < endTime.Value))));
                            break;
                        case SiteType.FinalInspection:
                            finalComponents = components.Where(_ => string.Equals(_.OrderNo, curOrderNo, StringComparison.CurrentCultureIgnoreCase)
                                && (!hasTimeRange ? _.FinalInspectionTime.HasValue : (_.FinalInspectionTime >= startTime.Value && _.FinalInspectionTime < endTime.Value))).ToList();
                            dict.Add(siteType.Value, finalComponents.Count);
                            break;
                        case SiteType.Packing:
                            dict.Add(siteType.Value, components.Count(_ => string.Equals(_.OrderNo, curOrderNo, StringComparison.CurrentCultureIgnoreCase)
                                && (!hasTimeRange ? _.PackTime.HasValue : (_.PackTime >= startTime.Value && _.PackTime < endTime.Value))));
                            break;
                    }
                }
                dict.Add("封损", 0);
                dict.Add("未包装", finalComponents.Count(_ => !_.PackTime.HasValue));

                foreach (var power in powerLevels)
                {
                    dict.Add(power.HasValue ? power.ToString() : "无功率", ivComponents.Count(_ => _.IVPowerLevel == power));
                }

                foreach (var level in qLevels)
                {
                    var curComponents = finalComponents.Where(_ => string.Equals(_.QLevel, level, StringComparison.CurrentCultureIgnoreCase)).ToList();
                    var value = curComponents.Count.ToString();
                    if (needLevelPowers)
                    {
                        var powerList = curComponents.Where(_ => _.IVPowerLevel.HasValue).GroupBy(_ => _.IVPowerLevel).Select(_ => $"{(int)_.Key}:{_.Count()}").ToList();
                        if (curComponents.Any(_ => !_.IVPowerLevel.HasValue))
                        {
                            powerList.Add($"空:{curComponents.Count(_ => !_.IVPowerLevel.HasValue)}");
                        }
                        value = $"{value}[{string.Join("；", powerList)}]";
                    }

                    dict.Add(string.IsNullOrWhiteSpace(level) ? "无等级" : (string.Equals(level, "BF", StringComparison.CurrentCultureIgnoreCase) ? "报废" : level), value);
                }

                result.Add(dict);
            }

            return new Tuple<List<string>, List<Dictionary<string, object>>>(headers, result);
        }

        /// <summary>
        /// 查询过站统计
        /// </summary>
        /// <param name="workshopCode">车间代码</param>
        /// <param name="orderNo">订单号</param>
        /// <param name="workOrderNo">工单号</param>
        /// <param name="startTime">开始时间</param>
        /// <param name="endTime">截止时间</param>
        /// <returns></returns>
        public List<KeyValuePair<string, List<VComponent>>> QueryViaSiteAnalysis(string workshopCode, string orderNo, string workOrderNo, int lineId, DateTime? startTime, DateTime? endTime)
        {
            var result = new List<KeyValuePair<string, List<VComponent>>>();
            if (string.IsNullOrWhiteSpace(orderNo) && string.IsNullOrWhiteSpace(workOrderNo) && !startTime.HasValue)
            {
                return result;
            }

            var components = new List<VComponent>();
            using (var context = new MESContext("MES"))
            {
                using (var ts = new TransactionScope(TransactionScopeOption.Required,
                    new TransactionOptions { IsolationLevel = IsolationLevel.ReadUncommitted }))
                {
                    var query = context.Components.AsNoTracking().Where(_ => _.WorkshopCode == workshopCode && _.DistributeTime.HasValue && _.Valid);
                    if (!string.IsNullOrWhiteSpace(orderNo))
                    {
                        query = query.Where(_ => _.OrderNo == orderNo);
                    }
                    if (!string.IsNullOrWhiteSpace(workOrderNo))
                    {
                        query = query.Where(_ => _.WorkOrderNo == workOrderNo);
                    }
                    if (lineId > 0)
                    {
                        query = query.Where(_ => _.LineId == lineId);
                    }
                    if (startTime.HasValue && endTime.HasValue)
                    {
                        query = query.Where(_ => _.DistributeTime.Value >= startTime.Value && _.DistributeTime.Value <= endTime.Value);
                    }
                    components.AddRange(Mapper.Map<List<VComponent>>(query.ToList()));
                    ts.Complete();
                }
            }

            result.Add(new KeyValuePair<string, List<VComponent>>("分片", components));
            result.Add(new KeyValuePair<string, List<VComponent>>("焊接", components.Where(_ => _.WeldingTime.HasValue).ToList()));
            result.Add(new KeyValuePair<string, List<VComponent>>("叠层", components.Where(_ => _.LaminatedTime.HasValue).ToList()));
            result.Add(new KeyValuePair<string, List<VComponent>>("EL1", components.Where(_ => _.EL1Time.HasValue).ToList()));
            result.Add(new KeyValuePair<string, List<VComponent>>("层压", components.Where(_ => _.TopTime.HasValue).ToList()));
            result.Add(new KeyValuePair<string, List<VComponent>>("NQC", components.Where(_ => _.NQCTime.HasValue).ToList()));
            result.Add(new KeyValuePair<string, List<VComponent>>("装框/线盒", components.Where(_ => _.FramingTime.HasValue).ToList()));
            result.Add(new KeyValuePair<string, List<VComponent>>("清洗", components.Where(_ => _.CleanTime.HasValue).ToList()));
            result.Add(new KeyValuePair<string, List<VComponent>>("IV测试", components.Where(_ => _.IVTestTime.HasValue).ToList()));
            result.Add(new KeyValuePair<string, List<VComponent>>("绝缘耐压", components.Where(_ => _.InsulatedResistanceTime.HasValue).ToList()));
            result.Add(new KeyValuePair<string, List<VComponent>>("EL2", components.Where(_ => _.EL2Time.HasValue).ToList()));
            result.Add(new KeyValuePair<string, List<VComponent>>("铭牌打印", components.Where(_ => _.NameplatePrintingTime.HasValue).ToList()));
            result.Add(new KeyValuePair<string, List<VComponent>>("终检", components.Where(_ => _.FinalInspectionTime.HasValue).ToList()));
            result.Add(new KeyValuePair<string, List<VComponent>>("包装", components.Where(_ => _.PackTime.HasValue).ToList()));
            result.Add(new KeyValuePair<string, List<VComponent>>("入库", components.Where(_ => _.InboundTime.HasValue).ToList()));

            return result;
        }

        /// <summary>
        /// 查询过站统计
        /// </summary>
        /// <param name="barCodes">批量组件条码</param>
        /// <returns>报表</returns>
        public List<KeyValuePair<string, List<VComponent>>> QueryViaSiteAnalysis(List<string> barCodes)
        {
            var result = new List<KeyValuePair<string, List<VComponent>>>();
            var entities = mesDb.Components.AsNoTracking().Where(_ => barCodes.Contains(_.BarCode) && _.DistributeTime.HasValue && _.Valid).ToList();
            var components = Mapper.Map<List<VComponent>>(entities);

            result.Add(new KeyValuePair<string, List<VComponent>>("分片", components));
            result.Add(new KeyValuePair<string, List<VComponent>>("焊接", components.Where(_ => _.WeldingTime.HasValue).ToList()));
            result.Add(new KeyValuePair<string, List<VComponent>>("叠层", components.Where(_ => _.LaminatedTime.HasValue).ToList()));
            result.Add(new KeyValuePair<string, List<VComponent>>("EL1", components.Where(_ => _.EL1Time.HasValue).ToList()));
            result.Add(new KeyValuePair<string, List<VComponent>>("层压", components.Where(_ => _.TopTime.HasValue).ToList()));
            result.Add(new KeyValuePair<string, List<VComponent>>("NQC", components.Where(_ => _.NQCTime.HasValue).ToList()));
            result.Add(new KeyValuePair<string, List<VComponent>>("装框/线盒", components.Where(_ => _.FramingTime.HasValue).ToList()));
            result.Add(new KeyValuePair<string, List<VComponent>>("清洗", components.Where(_ => _.CleanTime.HasValue).ToList()));
            result.Add(new KeyValuePair<string, List<VComponent>>("IV测试", components.Where(_ => _.IVTestTime.HasValue).ToList()));
            result.Add(new KeyValuePair<string, List<VComponent>>("绝缘耐压", components.Where(_ => _.InsulatedResistanceTime.HasValue).ToList()));
            result.Add(new KeyValuePair<string, List<VComponent>>("EL2", components.Where(_ => _.EL2Time.HasValue).ToList()));
            result.Add(new KeyValuePair<string, List<VComponent>>("铭牌打印", components.Where(_ => _.NameplatePrintingTime.HasValue).ToList()));
            result.Add(new KeyValuePair<string, List<VComponent>>("终检", components.Where(_ => _.FinalInspectionTime.HasValue).ToList()));
            result.Add(new KeyValuePair<string, List<VComponent>>("包装", components.Where(_ => _.PackTime.HasValue).ToList()));
            result.Add(new KeyValuePair<string, List<VComponent>>("入库", components.Where(_ => _.InboundTime.HasValue).ToList()));

            return result;
        }

        /// <summary>
        /// 查询过站分析详情列表
        /// </summary>
        /// <param name="page"></param>
        /// <param name="limit"></param>
        /// <param name="total"></param>
        /// <param name="siteType"></param>
        /// <param name="hasViaSite"></param>
        /// <param name="workshopCode"></param>
        /// <param name="orderNo"></param>
        /// <param name="workOrderNo"></param>
        /// <param name="startTime"></param>
        /// <param name="endTime"></param>
        /// <returns></returns>
        public List<VComponent> QueryViaSiteAnalyseDetails(int page, int limit, out int total, string siteType, bool hasViaSite, string workshopCode, string orderNo, string workOrderNo, int lineId, DateTime? startTime, DateTime? endTime)
        {
            if (string.IsNullOrWhiteSpace(orderNo) && string.IsNullOrWhiteSpace(workOrderNo) && !startTime.HasValue || string.IsNullOrWhiteSpace(siteType))
            {
                total = 0;
                return new List<VComponent>();
            }
            var query = mesDb.Components.AsNoTracking().Where(_ => _.WorkshopCode == workshopCode && _.DistributeTime.HasValue && _.Valid);
            if (!string.IsNullOrWhiteSpace(orderNo))
            {
                query = query.Where(_ => _.OrderNo == orderNo);
            }
            if (!string.IsNullOrWhiteSpace(workOrderNo))
            {
                query = query.Where(_ => _.WorkOrderNo == workOrderNo);
            }
            if (lineId > 0)
            {
                query = query.Where(_ => _.LineId == lineId);
            }
            if (startTime.HasValue && endTime.HasValue)
            {
                query = query.Where(_ => _.DistributeTime.Value >= startTime.Value && _.DistributeTime.Value <= endTime.Value);
            }
            switch (siteType)
            {
                case "分片":
                    break;
                case "焊接":
                    query = query.Where(_ => _.WeldingTime.HasValue == hasViaSite);
                    break;
                case "叠层":
                    query = query.Where(_ => _.LaminatedTime.HasValue == hasViaSite);
                    break;
                case "EL1":
                    query = query.Where(_ => _.EL1Time.HasValue == hasViaSite);
                    break;
                case "层压":
                    query = query.Where(_ => _.TopTime.HasValue == hasViaSite);
                    break;
                case "NQC":
                    query = query.Where(_ => _.NQCTime.HasValue == hasViaSite);
                    break;
                case "装框/线盒":
                    query = query.Where(_ => _.FramingTime.HasValue == hasViaSite);
                    break;
                case "清洗":
                    query = query.Where(_ => _.CleanTime.HasValue == hasViaSite);
                    break;
                case "IV测试":
                    query = query.Where(_ => _.IVTestTime.HasValue == hasViaSite);
                    break;
                case "绝缘耐压":
                    query = query.Where(_ => _.FinalInspectionTime.HasValue == hasViaSite);
                    break;
                case "EL2":
                    query = query.Where(_ => _.EL2Time.HasValue == hasViaSite);
                    break;
                case "铭牌打印":
                    query = query.Where(_ => _.NameplatePrintingTime.HasValue == hasViaSite);
                    break;
                case "终检":
                    query = query.Where(_ => _.FinalInspectionTime.HasValue == hasViaSite);
                    break;
                case "包装":
                    query = query.Where(_ => _.PackTime.HasValue == hasViaSite);
                    break;
            }

            total = query.Count();
            query = query.OrderBy(_ => _.PkId);
            var components = limit <= 0 ? query.ToList() : query.OrderBy(_ => _.PkId).Skip((page - 1) * limit).Take(limit).ToList();
            return Mapper.Map<List<VComponent>>(components);
        }

        /// <summary>
        /// 查询组件包装报表
        /// </summary>
        /// <param name="page"></param>
        /// <param name="limit"></param>
        /// <param name="total"></param>
        /// <param name="workshopCode"></param>
        /// <param name="orderNo"></param>
        /// <param name="workOrderNo"></param>
        /// <param name="startTime"></param>
        /// <param name="endTime"></param>
        /// <returns></returns>
        public List<RComponentPack> QueryComponentPacks(int page, int limit, out int total, string workshopCode, string orderNo, string workOrderNo, DateTime? startTime, DateTime? endTime)
        {
            total = 0;
            if (string.IsNullOrWhiteSpace(orderNo) && string.IsNullOrWhiteSpace(workOrderNo) && !startTime.HasValue && !endTime.HasValue)
            {
                return new List<RComponentPack>();
            }
            var query = mesDb.Components.AsNoTracking().Where(_ => _.DistributeTime <= endTime && _.LastSiteTime >= startTime && _.PackTime.HasValue && _.Valid);
            if (!string.IsNullOrWhiteSpace(workshopCode))
            {
                query = query.Where(_ => _.WorkshopCode == workshopCode);
            }
            if (!string.IsNullOrWhiteSpace(orderNo))
            {
                query = query.Where(_ => _.OrderNo == orderNo);
            }
            if (!string.IsNullOrWhiteSpace(workOrderNo))
            {
                query = query.Where(_ => _.WorkOrderNo == workOrderNo);
            }
            if (startTime.HasValue && endTime.HasValue)
            {
                query = query.Where(_ => _.PackTime >= startTime.Value && _.PackTime < endTime.Value);
            }

            total = query.Count();
            query = query.OrderBy(_ => _.PackTime);

            var components = limit <= 0 ? query.ToList() : query.Skip((page - 1) * limit).Take(limit).ToList();

            var result = components.Select(_ => new RComponentPack
            {
                BarCode = _.BarCode,
                CurrentLevel = _.IVCurrentLevel,
                PowerLevel = _.IVPowerLevel ?? 0,
                PackNo = _.PackNo,
                PackTime = _.PackTime.Value,
                WorkOrderNo = _.WorkOrderNo,
                PackDuty = _.PackDuty,
                NameplateModel = _.NameplateModel,
                OrderNo = _.OrderNo,
                QLevel = _.QLevel,
                FF = _.FF ?? 0,
                Imp = _.Im ?? 0,
                Isc = _.Isc ?? 0,
                Pmax = _.Pmax ?? 0,
                Voc = _.Voc ?? 0,
                Vpm = _.Vm ?? 0,
                ConversionEfficiency = _.ConversionEfficiency,
                CellEfficiency = _.CellEfficiency,
                Ress = _.Ress ?? 0,
                Ressh = _.Ressh ?? 0,
                SunRef = _.SunRef ?? 0
            }).ToList();

            return result;
        }

        /// <summary>
        /// 查询不良报表
        /// </summary>
        /// <param name="page"></param>
        /// <param name="limit"></param>
        /// <param name="total"></param>
        /// <param name="workshopCode"></param>
        /// <param name="orderNo"></param>
        /// <param name="workOrderNo"></param>
        /// <param name="startTime"></param>
        /// <param name="endTime"></param>
        /// <returns></returns>
        public List<RBadReportItem> QueryBadReports(int page, int limit, out int total, string workshopCode, string orderNo, string workOrderNo, DateTime? startTime, DateTime? endTime)
        {
            total = 0;
            if (string.IsNullOrWhiteSpace(orderNo) && string.IsNullOrWhiteSpace(workOrderNo) && !startTime.HasValue && !endTime.HasValue)
            {
                return new List<RBadReportItem>();
            }
            var query = mesDb.Components.AsNoTracking().Where(_ => _.QLevel != "" && _.QLevel != "Q1" && _.Valid);
            if (!string.IsNullOrWhiteSpace(workshopCode))
            {
                query = query.Where(_ => _.WorkshopCode == workshopCode);
            }
            if (!string.IsNullOrWhiteSpace(orderNo))
            {
                query = query.Where(_ => _.OrderNo == orderNo);
            }
            if (!string.IsNullOrWhiteSpace(workOrderNo))
            {
                query = query.Where(_ => _.WorkOrderNo == workOrderNo);
            }
            if (startTime.HasValue && endTime.HasValue)
            {
                query = query.Where(_ => _.ScrapTime >= startTime.Value && _.ScrapTime < endTime.Value);
            }

            total = query.Count();
            query = query.OrderBy(_ => _.PackTime);
            var components = limit <= 0 ? query.ToList() : query.Skip((page - 1) * limit).Take(limit).ToList();

            var badReasons = mesDb.BadReasons.AsNoTracking().Where(_ => _.Valid).ToList();

            var result = new List<RBadReportItem>();
            foreach (var _ in components)
            {
                var item = new RBadReportItem
                {
                    BarCode = _.BarCode,
                    OrderNo = _.OrderNo,
                    WorkOrderNo = _.WorkOrderNo,
                    PowerLevel = _.IVPowerLevel ?? 0,
                    CurrentLevel = _.IVCurrentLevel,
                    QLevel = _.QLevel,
                    NameplateModel = _.ProductStatus == ProductStatus.Scrapped ? "报废" : _.NameplateModel,
                    ComponentColor = _.ComponentColor,
                    EL2Level = !_.EL2OncePass.HasValue ? "" : (_.EL2OncePass == true ? "OK" : "NG"),
                    BadReason = _.DowngradeName,
                    IsMeteriaComeBad = badReasons.FirstOrDefault(b => StringUtils.Equals(_.DowngradeCode, b.ReasonCode))?.IsMaterielBad ?? false,
                    ComponentType = _.FinalInspectionComponentType,
                    CellFeature = _.FinalInspectionCellFeature,
                    DutyName = _.FinalInspectionDuty,
                    CreateTime = _.ScrapTime,
                    Power = _.Pmax ?? 0,
                    Operator = _.FinalInspectionUser
                };
                result.Add(item);
            }
            return result;
        }

        /// <summary>
        /// 查询CTM报表
        /// </summary>
        /// <param name="workshopCode">车间代码</param>
        /// <param name="workOrderNo">工单号</param>
        /// <param name="orderNo">订单号</param>
        /// <param name="startTime">起始时间</param>
        /// <param name="endTime">截止时间</param>
        /// <returns>CTM列表</returns>
        public List<RCTM> QueryCTMs(string workshopCode, string orderNo, string workOrderNo, DateTime? startTime, DateTime? endTime)
        {
            if (string.IsNullOrWhiteSpace(orderNo) && string.IsNullOrWhiteSpace(workOrderNo) && !startTime.HasValue && !endTime.HasValue)
            {
                return new List<RCTM>();
            }
            var componentQuery = mesDb.Components.AsNoTracking().Where(_ => _.WorkshopCode == workshopCode && _.IVPowerLevel > 0 && _.Valid);
            if (!string.IsNullOrWhiteSpace(orderNo))
            {
                componentQuery = componentQuery.Where(_ => _.OrderNo == orderNo);
            }
            if (!string.IsNullOrWhiteSpace(workOrderNo))
            {
                componentQuery = componentQuery.Where(_ => _.WorkOrderNo == workOrderNo);
            }
            if (startTime.HasValue && endTime.HasValue)
            {
                componentQuery = componentQuery.Where(_ => _.IVTestTime >= startTime && _.IVTestTime < endTime);
            }
            var components = componentQuery.ToList();

            var ctms = new List<RCTM>();
            foreach (var item in components.GroupBy(_ => new { _.OrderNo, _.BatteryFactory, _.BatterySpecs, _.CellEfficiency }))
            {
                var ctm = new RCTM
                {
                    OrderNo = item.Key.OrderNo,
                    BatteryFactory = item.Key.BatteryFactory,
                    BatterySpecs = item.Key.BatterySpecs,
                    CellEfficiency = item.Key.CellEfficiency,
                    AveragePower = Convert.ToDecimal(item.Average(_ => _.IVPowerLevel.Value).ToString("0.000")),
                    Quantity = item.Count(),
                    CTM = item.Sum(_ => _.TheoreticalPower) <= 0 ? 0 : Convert.ToDecimal((item.Sum(_ => _.IVPowerLevel.Value) / item.Sum(_ => _.TheoreticalPower) * 100).ToString("0.00")),
                    TheoreticalPower = item.FirstOrDefault().TheoreticalPower,
                    Desc = string.Join("；", item.GroupBy(_ => _.IVPowerLevel.Value).Select(_ => $"{_.Key}：{_.Count()}，{((decimal)_.Count() / item.Count() * 100).ToString("0.00")}%"))
                };
                ctms.Add(ctm);
            }

            return ctms;
        }

        /// <summary>
        /// 查询物料报表
        /// </summary>
        public Tuple<int, List<RMateriaModel>> QueryMaterioaReport(int page, int limit, string orderNo, string workOrderNo, string cabinetNo, string packNo, string barCode)
        {
            var total = 0;
            var models = new List<RMateriaModel>();
            using (var context = new MESContext("MES"))
            {
                using (var ts = new TransactionScope(TransactionScopeOption.Required,
                    new TransactionOptions { IsolationLevel = IsolationLevel.ReadUncommitted }))
                {
                    var query = context.Components.AsNoTracking().Where(_ => _.Valid);
                    if (!string.IsNullOrWhiteSpace(barCode))
                    {
                        query = query.Where(_ => _.BarCode == barCode);
                    }
                    else if (!string.IsNullOrWhiteSpace(packNo))
                    {
                        query = query.Where(_ => _.PackNo == packNo);
                    }
                    if (!string.IsNullOrWhiteSpace(cabinetNo))
                    {
                        query = query.Where(_ => _.CabinetNo == cabinetNo);
                    }
                    else if (!string.IsNullOrWhiteSpace(workOrderNo))
                    {
                        query = query.Where(_ => _.WorkOrderNo == workOrderNo);
                    }
                    else if (!string.IsNullOrWhiteSpace(orderNo))
                    {
                        query = query.Where(_ => _.OrderNo == orderNo);
                    }
                    total = page <= 0 ? 0 : query.Count();
                    query = query.OrderByDescending(_ => _.PkId);
                    var skip = (page - 1) * limit;
                    models = (page <= 0 ? query : query.Skip(skip).Take(limit)).Select(_ => new RMateriaModel
                    {
                        PkId = _.PkId,
                        OrderNo = _.OrderNo,
                        WorkOrderNo = _.WorkOrderNo,
                        WorkshopCode = _.WorkshopCode,
                        WorkshopName = _.WorkshopName,
                        CabinetNo = _.CabinetNo,
                        PackNo = _.PackNo,
                        BarCode = _.BarCode,
                        ProductCode = _.ProductCode,
                        ProductSpecs = _.ProductSpecs,
                        BatteryCode = _.BatteryCode,
                        BatterySpecs = _.BatterySpecs,
                        BatteryCompany = _.BatteryFactory,
                        BatteryBatchNo = _.BatteryBatchNo,
                        GlassCode = _.GlassCode,
                        GlassSpecs = _.GlassSpecs,
                        GlassCompany = _.GlassFactory,
                        GlassBatchNo = _.GlassBatchNo,
                        BackPlanCode = _.BackPlanCode,
                        BackPlanSpecs = _.BackPlanSpecs,
                        BackPlanCompany = _.BackPlanFactory,
                        BackPlanBatchNo = _.BackPlanBatchNo,
                        EvaCode = _.EvaCode,
                        EvaSpecs = _.EvaSpecs,
                        EvaCompany = _.EvaCompany,
                        EvaBatchNo = _.EvaBatchNo,
                        RibbonCode = _.RibbonCode,
                        RibbonSpecs = _.RibbonSpecs,
                        RibbonCompany = _.RibbonCompany,
                        RibbonBatchNo = _.RibbonBatchNo,
                        LongFrameCode = _.LongFrameCode,
                        LongFrameSpecs = _.AluFrameSpecs,
                        LongFrameCompany = _.AluFrameFactory,
                        LongFrameBatchNo = _.AluFrameBatchNo,
                        ShortFrameCode = _.ShortFrameCode,
                        ShortFrameSpecs = _.ShortFrameSpecs,
                        ShortFrameCompany = _.ShortFrameCompany,
                        ShortFrameBatchNo = _.ShortFrameBatchNo,
                        BoxCode = _.JunBoxCode,
                        BoxSpecs = _.JunBoxSpecs,
                        BoxCompany = _.JunBoxFactory,
                        BoxBatchNo = _.JunBoxBatchNo
                    }).ToList();
                }
            }
            return new Tuple<int, List<RMateriaModel>>(total, models);
        }
    }
}
